package org.yidu.novel.repository;

import java.util.List;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.yidu.novel.bean.ArticleSearchBean;
import org.yidu.novel.entity.TArticle;
import org.yidu.novel.utils.Utils;

/**
 * <p>
 * 小说表操作类
 * </p>
 * Copyright(c) 2014-2017 YiDu-Novel. All rights reserved.
 * 
 * @version 2.0.0
 * @author shinpa.you
 */
public interface TArticleRepository extends PagingAndSortingRepository<TArticle, Long>, JpaSpecificationExecutor<TArticle> {
    /**
     * 通过条件查找小说 <br>
     * Specifications没有序列化，不能直接用来当缓存的key，所以封装了一层
     * 
     * @param searchBean
     *            检索条件
     * @param pageable
     *            分页信息
     * @return 小说列表
     */
    @Cacheable(value = "yiduCache", key = "{'org.yidu.novel.repository.TArticleRepository.findAll(Specification<TArticle>, Pageable)', #root.args[0], #root.args[1] }")
    default Page<TArticle> findAllWithSearchBean(ArticleSearchBean searchBean, Pageable pageable) {
        return findAll(Specifications.where(ArticleSpecifications.equalCategory(searchBean.getCategory()))
                .and(ArticleSpecifications.equalFinish(searchBean.getFullFlag())).and(ArticleSpecifications.gtSize(0)), pageable);
    }

    /**
     * 通过小说名查找小说信息
     * 
     * @param articleName
     *            小说名
     * @return 小说列表
     */
    @Cacheable(value = "yiduCache", key = "{'org.yidu.novel.repository.TArticleRepository.findByArticleName(String)', #root.args[0] }")
    List<TArticle> findByArticleName(String articleName);

    /**
     * 小说排行
     * 
     * @param pageable
     *            排序信息
     * @return 小说列表
     */
    @Cacheable(value = "yiduCache", key = "{'org.yidu.novel.repository.TArticleRepository.findByLastChapterIsNotNull(Pageable)', #root.args[0] }")
    Page<TArticle> findByLastChapterIsNotNull(Pageable pageable);

    /**
     * 通过小说名来查找小说数
     * 
     * @param articleName
     * @return 小说数量
     */
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.countByArticleName(String)', #articleName }")
    Integer countByArticleName(String articleName);

    /**
     * 通过拼音来查找小说信息
     * 
     * @param pinyin
     * @return 小说数量
     */
    // @Query("FROM TArticle WHERE pinyin = ?1 ")
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findByPinyin(String)', #pinyin }")
    List<TArticle> findByPinyin(String pinyin);

    /**
     * 通过多个小说编号查找小说信息
     * 
     * @param articleNos
     * @return 小说信息列表
     */
    // @Query("FROM TArticle WHERE articleNo in ?1 and size > 0 ")
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findByArticleNoContainsAndLastChapterIsNotNull(List<Long>)', #articleNoList }")
    List<TArticle> findByArticleNoInAndLastChapterIsNotNull(List<Long> articleNoList);

    /**
     * 按作者获取小说
     * 
     * @param pageable
     * @return 小说列表
     */
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findByAuthorAndLastChapterIsNotNull(String, Pageable)', #root.args[0], #root.args[1] }")
    Page<TArticle> findByAuthorAndLastChapterIsNotNull(String author, Pageable pageable);

    /**
     * 按小说分类获取小说
     * 
     * @param pageable
     * @return 小说列表
     */
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findByCategoryOrderByAllVisitDesc(Integer, Pageable)', #root.args[0], #root.args[1] }")
    Page<TArticle> findByCategoryAndLastChapterIsNotNullOrderByAllVisitDesc(Integer category, Pageable pageable);

    /**
     * 获取随机小说列表
     * 
     * @param pageable
     * @return 小说信息列表
     */
    // TODO order by 会导致Seq Scan，要优化
    // @Query("FROM TArticle order by random() ")
    @Query(value = "select * from t_article offset random() * (select count(*) FROM t_article) ORDER BY ?#{#pageable};", nativeQuery = true)
    // explain analyze select * from t_article where articleno in (SELECT
    // floor(random() * (select max(articleno) from t_article )) FROM
    // generate_series(1,10))
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findRandomRecommendArticleList(Pageable)', #root.args[0] }")
    List<TArticle> findRandomRecommendArticleList(Pageable pageable);

    /**
     * 按小说名获取小说
     * 
     * @param pageable
     * @return 小说列表
     */
    @Query("FROM TArticle")
    @Cacheable(value = "yiduCache", key = "{ 'org.yidu.novel.repository.TArticleRepository.findByAuthorContainsOrderByArticleNoAsc(Pageable)', #root.args[0] }")
    List<TArticle> findRelativeArticleList(Pageable pageable);

    /**
     * <p>
     * 多条件用Specification定义
     * </p>
     * Copyright(c) 2014-2017 YiDu-Novel. All rights reserved.
     * 
     * @version 2.0.0
     * @author shinpa.you
     */
    public class ArticleSpecifications {
        // public static Specification<TArticle> idLessThanOrEqualTo(final Long
        // id) {
        // return new Specification<TArticle>() {
        // @Override
        // public Predicate toPredicate(Root<TArticle> root, CriteriaQuery<?>
        // query, CriteriaBuilder cb) {
        // return cb.lessThanOrEqualTo(root.get(TArticle.id), id);
        // }
        // };
        // }
        //
        // public static Specification<TArticle> hasDept(final Dept dept) {
        // return new Specification<TArticle>() {
        // @Override
        // public Predicate toPredicate(Root<TArticle> root, CriteriaQuery<?>
        // query, CriteriaBuilder cb) {
        // return cb.equal(root.get(Emp_.dept), dept);
        // }
        // };
        // }

        public static Specification<TArticle> equalCategory(Integer category) {
            return !Utils.isDefined(category) ? null : (root, query, cb) -> {
                return cb.equal(root.get("category"), category);
            };
        }

        public static Specification<TArticle> equalFinish(Boolean fullFlag) {
            return !Utils.isDefined(fullFlag) ? null : (root, query, cb) -> {
                return cb.equal(root.get("fullFlag"), fullFlag);
            };
        }

        public static Specification<TArticle> gtSize(Integer size) {
            return !Utils.isDefined(size) ? null : (root, query, cb) -> {
                return cb.gt(root.get("size"), size);
            };
        }

        public static Specification<TArticle> isNotNullLastChapter() {
            return (root, query, cb) -> {
                return cb.isNotNull(root.get("lastChapter"));
            };
        }

    }

    @Modifying
    @Query("update TArticle a set a.dayVisit = a.dayVisit+1 , a.weekVisit = a.weekVisit+1 , a.monthVisit = a.monthVisit+1 , a.allVisit = a.allVisit+1 where a.articleNo = ?1 ")
    public int updateStatisticsInfo( Long articleNo);
//    @Param("name") String name
    
    //And --- 等价于 SQL 中的 and 关键字，比如 findByUsernameAndPassword(String user, Striang pwd)；
    //Or --- 等价于 SQL 中的 or 关键字，比如 findByUsernameOrAddress(String user, String addr)；
    //Between --- 等价于 SQL 中的 between 关键字，比如 findBySalaryBetween(int max, int min)；
    //LessThan --- 等价于 SQL 中的 "<"，比如 findBySalaryLessThan(int max)；
    //GreaterThan --- 等价于 SQL 中的">"，比如 findBySalaryGreaterThan(int min)；
    //IsNull --- 等价于 SQL 中的 "is null"，比如 findByUsernameIsNull()；
    //IsNotNull --- 等价于 SQL 中的 "is not null"，比如 findByUsernameIsNotNull()；
    //NotNull --- 与 IsNotNull 等价；
    //Like --- 等价于 SQL 中的 "like"，比如 findByUsernameLike(String user)；
    //NotLike --- 等价于 SQL 中的 "not like"，比如 findByUsernameNotLike(String user)；
    //OrderBy --- 等价于 SQL 中的 "order by"，比如 findByUsernameOrderBySalaryAsc(String user)；
    //Not --- 等价于 SQL 中的 "！ ="，比如 findByUsernameNot(String user)；
    //In --- 等价于 SQL 中的 "in"，比如 findByUsernameIn(Collection<String> userList) ，方法的参数可以是 Collection 类型，也可以是数组或者不定长参数；
    //NotIn --- 等价于 SQL 中的 "not in"，比如 findByUsernameNotIn(Collection<String> userList) ，方法的参数可以是 Collection 类型，也可以是数组或者不定长参数；

}
